﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace EntModApp.EntServices.Data
{
  public static class SimpleFilterExpressionExtensions
  {
    public const string ParameterName = "e";

    public static Expression<Func<TEntity, bool>> ToLambda<TEntity>(this Expression body, ParameterExpression parameter)
    {
      return Expression.Lambda<Func<TEntity, bool>>(body, parameter);
    }

    public static Expression CreateExpressionBody<TEntity, TValue>(this SimpleFilterExpression<TValue> expression, PropertyInfo property, ParameterExpression parameter) 
    {
      if (expression is SimpleComparisonFilterExpression<TValue>)
        return ((SimpleComparisonFilterExpression<TValue>)expression).CreateExpressionBody<TEntity, TValue>(property, parameter);
      if (expression is SimpleStringFilterExpression)
        return ((SimpleStringFilterExpression)(object)expression).CreateExpressionBody<TEntity, string>(property, parameter);
      if (expression is BetweenValuesFilterExpression<TValue>)
        return ((BetweenValuesFilterExpression<TValue>)expression).CreateExpressionBody<TEntity, TValue>(property, parameter);
      throw new NotSupportedException();
    }
    public static Expression CreateExpressionBody<TEntity, TValue>(this SimpleFilterExpression<TValue> expression, string propertyName, ParameterExpression parameter) 
    {
      if (expression is SimpleComparisonFilterExpression<TValue>)
        return ((SimpleComparisonFilterExpression<TValue>)expression).CreateExpressionBody<TEntity, TValue>(propertyName, parameter);
      if (expression is SimpleStringFilterExpression)
        return ((SimpleStringFilterExpression)(object)expression).CreateExpressionBody<TEntity, string>(propertyName, parameter);
      if (expression is BetweenValuesFilterExpression<TValue>)
        return ((BetweenValuesFilterExpression<TValue>)expression).CreateExpressionBody<TEntity, TValue>(propertyName, parameter);
      throw new NotSupportedException();
    }

    public static Expression CreateExpressionBody<TEntity, TValue>(this SimpleComparisonFilterExpression<TValue> expression, PropertyInfo property, ParameterExpression parameter) 
    {
      ExpressionType expressionType;
      switch (expression.Operation)
      {
        case ComparisonFilterOperation.Equals:
          expressionType = ExpressionType.Equal;
          break;
        case ComparisonFilterOperation.NotEqual:
          expressionType = ExpressionType.NotEqual;
          break;
        case ComparisonFilterOperation.LessThan:
          expressionType = ExpressionType.LessThan;
          break;
        case ComparisonFilterOperation.LessThanOrEqualsTo:
          expressionType = ExpressionType.LessThanOrEqual;
          break;
        case ComparisonFilterOperation.GreaterThan:
          expressionType = ExpressionType.GreaterThan;
          break;
        case ComparisonFilterOperation.GreaterThanOrEqualsTo:
          expressionType = ExpressionType.GreaterThanOrEqual;
          break;
        default:
          throw new ArgumentException("Invalid operation type: " + expression.Operation);
      }

      var result = Expression.MakeBinary(
          expressionType,
          Expression.Property(
            parameter,
            property
          ),
          Expression.Constant(expression.Operand, typeof(TValue))
        );

      return result;
    }
    public static Expression CreateExpressionBody<TEntity, TValue>(this SimpleComparisonFilterExpression<TValue> expression, string propertyName, ParameterExpression parameter) 
    {
      ExpressionType expressionType;
      switch (expression.Operation)
      {
        case ComparisonFilterOperation.Equals:
          expressionType = ExpressionType.Equal;
          break;
        case ComparisonFilterOperation.NotEqual:
          expressionType = ExpressionType.NotEqual;
          break;
        case ComparisonFilterOperation.LessThan:
          expressionType = ExpressionType.LessThan;
          break;
        case ComparisonFilterOperation.LessThanOrEqualsTo:
          expressionType = ExpressionType.LessThanOrEqual;
          break;
        case ComparisonFilterOperation.GreaterThan:
          expressionType = ExpressionType.GreaterThan;
          break;
        case ComparisonFilterOperation.GreaterThanOrEqualsTo:
          expressionType = ExpressionType.GreaterThanOrEqual;
          break;
        default:
          throw new ArgumentException("Invalid operation type: " + expression.Operation);
      }

      var result = Expression.MakeBinary(
          expressionType,
          Expression.Property(
            parameter,
            propertyName
          ),
          Expression.Constant(expression.Operand, typeof(TValue))
        );

      return result;
    }

    public static Expression CreateExpressionBody<TEntity>(this SimpleStringFilterExpression expression, PropertyInfo property, ParameterExpression parameter)
    {
      Expression<Func<string, bool>> methodExpression;
      switch (expression.Operation)
      {
        case StringFilterOperation.StartsWith:
          methodExpression = s => s.StartsWith("");
          break;
        case StringFilterOperation.EndsWith:
          methodExpression = s => s.EndsWith("");
          break;
        case StringFilterOperation.Contains:
          methodExpression = s => s.Contains("");
          break;
        default:
          throw new ArgumentException("Invalid operation type: " + expression.Operation);
      }
      MethodInfo method = ((MethodCallExpression)methodExpression.Body).Method;

      var result = Expression.Call(
          Expression.Property(
            parameter,
            property
          ),
          method,
          Expression.Constant(expression.Operand, typeof(string))
        );

      return result;
    }
    public static Expression CreateExpressionBody<TEntity>(this SimpleStringFilterExpression expression, string propertyName, ParameterExpression parameter)
    {
      Expression<Func<string, bool>> methodExpression;
      switch (expression.Operation)
      {
        case StringFilterOperation.StartsWith:
          methodExpression = s => s.StartsWith("");
          break;
        case StringFilterOperation.EndsWith:
          methodExpression = s => s.EndsWith("");
          break;
        case StringFilterOperation.Contains:
          methodExpression = s => s.Contains("");
          break;
        default:
          throw new ArgumentException("Invalid operation type: " + expression.Operation);
      }
      MethodInfo method = ((MethodCallExpression)methodExpression.Body).Method;

      var result = Expression.Call(
          Expression.Property(
            parameter,
            propertyName
          ),
          method,
          Expression.Constant(expression.Operand, typeof(string))
        );

      return result;
    }

    public static Expression CreateExpressionBody<TEntity, TValue>(this BetweenValuesFilterExpression<TValue> expression, PropertyInfo property, ParameterExpression parameter) 
    {
      ExpressionType expression1Type = expression.Include1 ? ExpressionType.GreaterThanOrEqual : ExpressionType.GreaterThan;
      ExpressionType expression2Type = expression.Include2 ? ExpressionType.LessThanOrEqual : ExpressionType.LessThan;

      var result = Expression.MakeBinary(
          ExpressionType.AndAlso,
          Expression.MakeBinary(
            expression1Type,
            Expression.Property(
              parameter,
              property
            ),
            Expression.Constant(expression.Operand1, typeof(TValue))
          ),
          Expression.MakeBinary(
            expression2Type,
            Expression.Property(
              parameter,
              property
            ),
            Expression.Constant(expression.Operand2, typeof(TValue))
          )
        );

      return result;
    }
    public static Expression CreateExpressionBody<TEntity, TValue>(this BetweenValuesFilterExpression<TValue> expression, string propertyName, ParameterExpression parameter) 
    {
      ExpressionType expression1Type = expression.Include1 ? ExpressionType.GreaterThanOrEqual : ExpressionType.GreaterThan;
      ExpressionType expression2Type = expression.Include2 ? ExpressionType.LessThanOrEqual : ExpressionType.LessThan;

      var result = Expression.MakeBinary(
          ExpressionType.AndAlso,
          Expression.MakeBinary(
            expression1Type,
            Expression.Property(
              parameter,
              propertyName
            ),
            Expression.Constant(expression.Operand1, typeof(TValue))
          ),
          Expression.MakeBinary(
            expression2Type,
            Expression.Property(
              parameter,
              propertyName
            ),
            Expression.Constant(expression.Operand2, typeof(TValue))
          )
        );

      return result;
    }

  }
}
